from typing import Dict, Any, Optional, List, Tuple
import config
import db
import httpx
import asyncio
import random
from .model.request import SearchRequest, NoteRequest, UserRequest
from pkg.account_pool.pool import AccountWithIpPoolManager
from pkg.proxy.proxy_ip_pool import ProxyIpPool, create_ip_pool
from pkg.tools import utils
from repo.platform_save_data import xhs as xhs_store
from ..client import XiaoHongShuClient
from ..field import SearchSortType
from ..help import parse_creator_info_from_creator_url, parse_note_info_from_note_url
import constant
from var import media_crawler_db_var, db_conn_pool_var, crawler_type_var, source_keyword_var
from .transform import XHSDataTransformer
from .model.response import NotesResult, NoteDetail, Hots, CommentInfo, SubCommentInfo, UserInfo
from .mock import MOCK_SEARCH_RESULT
from model.request import BaseRequest
import json

class XHSApiCore:
    def __init__(self):
        self.client = None
        self.xhs_client = XiaoHongShuClient()
        
    async def initialize(self):
        """初始化资源"""
        try:
            # 1. 初始化HTTP客户端
            self.client = httpx.AsyncClient(verify=False)
            
            # 2. 设置必要的上下文变量
            crawler_type_var.set("api")
            source_keyword_var.set("")  # 设置一个空值
            
            # 3. 初始化数据库（如果需要）
            if self.save_mode == "db":
                pool = await db.init_db()
                if pool:
                    db_conn_pool_var.set(pool)
                    media_crawler_db_var.set(pool)
            
            # 4. 账号池和IP池的初始化
            proxy_ip_pool: Optional[ProxyIpPool] = None
            if config.ENABLE_IP_PROXY:
                # xhs对代理验证还行，可以选择长时长的IP，比如30分钟一个IP
                # 快代理：私密代理->按IP付费->专业版->IP有效时长为30分钟, 购买地址：https://www.kuaidaili.com/?ref=ldwkjqipvz6c
                proxy_ip_pool = await create_ip_pool(
                    config.IP_PROXY_POOL_COUNT, enable_validate_ip=True
                )

            # 5. 初始化账号池
            account_with_ip_pool = AccountWithIpPoolManager(
                platform_name=constant.XHS_PLATFORM_NAME,
                account_save_type=config.ACCOUNT_POOL_SAVE_TYPE,
                proxy_ip_pool=proxy_ip_pool,
            )
            await account_with_ip_pool.async_initialize()

            # 6. 设置客户端并更新账号信息
            self.xhs_client.account_with_ip_pool = account_with_ip_pool
            await self.xhs_client.update_account_info()
                
        except Exception as e:
            utils.logger.error(f"[小红书API.初始化] 初始化失败: {e}")
            # 确保清理所有资源
            await self.cleanup()
            raise e

    async def cleanup(self):
        """清理资源"""
        try:
            # 1. 清理HTTP客户端
            if self.client:
                await self.client.aclose()
            
            # 2. 清理数据库连接
            if self.save_mode == "db":
                try:
                    await db.close()
                except Exception as e:
                    utils.logger.error(f"[小红书API.清理] 数据库关闭失败: {e}")
            
            # 3. 清理所有上下文变量
            try:
                crawler_type_var.set(None)
                source_keyword_var.set(None)
                media_crawler_db_var.set(None)
                db_conn_pool_var.set(None)
            except Exception as e:
                utils.logger.error(f"[小红书API.清理] 上下文变量清理失败: {e}")
                
        except Exception as e:
            utils.logger.error(f"[小红书API.清理] 资源清理失败: {e}")

    async def search_notes(self, search_request: SearchRequest) -> Tuple[NotesResult, str, int]:
        """搜索笔记"""
        try:
            utils.logger.info(f"[小红书API.搜索笔记] 请求参数为: {search_request}")
            # 先设置保存模式，再初始化
            self.save_mode = "db"
            await self.initialize()
            
            # 设置搜索关键词到上下文
            source_keyword_var.set(search_request.keyword)
            
            utils.logger.info(f"[小红书API.搜索笔记] 初始化完成")
            result, last_error,total_pages = await self._search_implementation(search_request)
            utils.logger.info(f"[小红书API.搜索笔记] 搜索完成")
            return result, last_error,total_pages
        except Exception as e:
            utils.logger.error(f"[小红书API.搜索笔记] 搜索失败: {e}")
            raise e
        finally:
            await self.cleanup()

    async def get_note_detail(self, note_requests: List[NoteRequest]) -> Tuple[List[NoteDetail], int, str]:
        """批量获取笔记详情"""
        try:
            self.save_mode = "db"
            await self.initialize()
            
            results, success_count, failed_msg = await self._get_note_detail_implementation(note_requests)
            
            await self.cleanup()
            return results, success_count, failed_msg
        except Exception as e:
            await self.cleanup()
            raise e

    async def get_hots(self, user_request: BaseRequest) -> List[Hots]:
        """获取热门榜单"""
        try:
            self.save_mode = "db"
            await self.initialize()

            result = await self._get_hots_implementation()

            await self.cleanup()
            return result
        except Exception as e:
            await self.cleanup()
            raise e



    async def get_note_comments(self, note_request: NoteRequest) -> Dict[str, Any]:
        """获取笔记评论"""
        try:
            self.save_mode = "db"
            await self.initialize()
            
            result = await self._get_comments_implementation(note_request)
            
            await self.cleanup()
            return result
        except Exception as e:
            await self.cleanup()
            raise e

    async def get_user_info(self, user_request: UserRequest) -> Dict[str, Any]:
        """获取用户信息"""
        try:
            self.save_mode = "db"
            await self.initialize()
            
            result = await self._get_user_info_implementation(user_request)
            
            await self.cleanup()
            return result
        except Exception as e:
            await self.cleanup()
            raise e

    # ===============================服务层实现===============================================

    async def _search_implementation(self, request: SearchRequest) -> Tuple[NotesResult, str, int]:
        """实现搜索笔记的具体逻辑"""
        try:
            utils.logger.info(f"[小红书API.搜索实现] 开始搜索关键词: {request.keyword}")
            
            # 每页20条笔记，根据用户请求的total计算需要搜索的页数
            xhs_limit_count = 20
            if request.total < xhs_limit_count:
                request.total = xhs_limit_count
            
            # 计算需要请求的总页数
            total_pages = (request.total + xhs_limit_count - 1) // xhs_limit_count
            
            # 初始化结果列表和错误信息
            all_notes = []
            has_more = True
            current_page = 1
            last_error = None
            error_page = None
            
            # 当收集的笔记数量小于请求总数且还有更多数据时继续请求
            while len(all_notes) < request.total and has_more and current_page <= total_pages:
                try:
                    # 获取原始搜索结果
                    notes_res = await self.xhs_client.get_note_by_keyword(
                        keyword=request.keyword,
                        page=current_page,
                        page_size=xhs_limit_count,
                        sort=SearchSortType(request.sort) if request.sort else SearchSortType.GENERAL
                    )
                    
                    # 如果是测试模式，使用模拟数据
                    if config.DEBUG:
                        notes_res = MOCK_SEARCH_RESULT.get("data", {})
                    
                    if not notes_res:
                        utils.logger.warning(f"[小红书API.搜索实现] 第{current_page}页未找到相关笔记")
                        # 如果是第一页就没有数据，直接返回空结果
                        if current_page == 1:
                            return NotesResult(
                                notes=[],
                                has_more=False,
                                page=0,
                                total=0,
                                error_msg="首页未找到数据"
                            )
                        break
                    
                    # 获取本次请求的笔记列表
                    current_notes = notes_res.get("items", [])
                    if not current_notes:
                        has_more = False
                        break
                        
                    all_notes.extend(current_notes)
                    
                    # 更新是否还有更多数据的标志
                    has_more = notes_res.get("has_more", False)
                    
                    utils.logger.info(f"[小红书API.搜索实现] 当前第{current_page}页，已获取 {len(all_notes)} 条笔记")
                    
                    # 更新页码
                    current_page += 1
                    
                    # 可以添加适当的延迟，避免请求过于频繁
                    await asyncio.sleep(random.uniform(1, 2))
                    
                except Exception as e:
                    error_msg = f"第{current_page}页请求失败: {str(e)}"
                    utils.logger.error(f"[小红书API.搜索实现] {error_msg}")
                    
                    # 记录最后一次错误信息和页码
                    last_error = error_msg
                    error_page = current_page
                    
                    # 如果是第一页就出错，直接抛出异常
                    if current_page == 1:
                        raise Exception(f"首页请求失败: {str(e)}")
                    
                    # 如果是中间页出错，结束循环但保留已获取的数据
                    has_more = False
                    break
            
            # 如果收集的笔记数量超过请求总数，只保留需要的数量
            if len(all_notes) > request.total:
                all_notes = all_notes[:request.total]
            
            # 转换搜索结果
            result = XHSDataTransformer.transform_notes_result({
                "items": all_notes,
                "has_more": has_more,
                "page": current_page - 1,  # 返回最后一次成功请求的页码
                "total": len(all_notes)
            })
            
            # 如果有错误信息，添加到结果中
            # if last_error:
            #     result.error_msg = last_error
            #     result.error_page = error_page
            
            utils.logger.info(
                f"[小红书API.搜索实现] 共搜索{current_page-1}页，找到 {len(all_notes)} 条笔记" + 
                (f"，最后错误: {last_error}" if last_error else "")
            )
            return result, last_error,total_pages

        except Exception as e:
            utils.logger.error(f"[小红书API.搜索实现] 搜索失败: {str(e)}")
            raise e

    async def _get_note_detail_implementation(self, requests: List[NoteRequest]) -> Tuple[List[NoteDetail], int, str]:
        """实现批量获取笔记详情的具体逻辑"""
        try:
            # 初始化结果列表
            results = []
            failed_links = []
            
            # 创建信号量控制并发
            semaphore = asyncio.Semaphore(config.MAX_CONCURRENCY_NUM)
            
            # 创建所有笔记的任务
            tasks = []
            for request in requests:
                task = self._process_single_note(request, semaphore)
                tasks.append(task)
            
            # 并发执行所有任务
            note_details = await asyncio.gather(*tasks, return_exceptions=True)
            
            # 处理结果
            for i, detail in enumerate(note_details):
                if isinstance(detail, Exception):
                    # 处理失败的请求
                    failed_links.append(f"{requests[i].link}(错误: {str(detail)})")
                elif detail:
                    results.append(detail)
                else:
                    failed_links.append(f"{requests[i].link}(错误: 笔记获取失败)")
            
            # 组装失败信息
            failed_msg = ""
            if failed_links:
                failed_msg = f"以下{len(failed_links)}条笔记获取失败：" + "；".join(failed_links)
            
            return results, len(results), failed_msg

        except Exception as e:
            utils.logger.error(f"[XHSApiCore._get_note_detail_implementation] Batch get note detail error: {e}")
            raise e

    async def _process_single_note(self, request: NoteRequest, semaphore: asyncio.Semaphore) -> Optional[NoteDetail]:
        """处理单个笔记的获取"""
        try:
            # 解析笔记链接获取信息
            note_url_info = parse_note_info_from_note_url(request.link)
            
            # 获取笔记详情
            note_detail = await self._get_note_detail_task(
                note_id=note_url_info.note_id,
                xsec_source=note_url_info.xsec_source,
                xsec_token=note_url_info.xsec_token,
                semaphore=semaphore,
                enable_comments=request.enable_note_comments
            )
            
            if not note_detail:
                return None

            # 转换详情
            detail = XHSDataTransformer.transform_note_detail(
                note=note_detail,
                from_html=True
            )
            
            if not detail:
                return None

            # 更新笔记链接和令牌信息
            if detail:
                detail.note.link = f"https://www.xiaohongshu.com/explore/{note_url_info.note_id}?xsec_token={note_url_info.xsec_token}"
                detail.note.xsec_token = note_url_info.xsec_token

            return detail

        except Exception as e:
            utils.logger.error(f"[XHSApiCore._process_single_note] Process note error: {e}")
            raise e

    async def _get_hots_implementation(self) -> List[Hots]:
        """实现获取热门榜单的具体逻辑"""
        try:
            utils.logger.info("[小红书API.热门榜单实现] 开始获取热门榜单")
            # 解析用户链接获取信息
            raw_data = await self.xhs_client.get_hots()
            result = []
            utils.logger.info(f"[小红书API.热门榜单实现] 原始数据: {raw_data}") 
            for hot in raw_data:
                hot_res = XHSDataTransformer.transform_hots(hot)
                if hot_res:
                    result.append(hot_res)
            utils.logger.info(f"[小红书API.热门榜单实现] 找到 {len(result)} 条热门榜单")
            return result
        except Exception as e:
            utils.logger.error(f"[XHSApiCore._get_hots_implementation] Get hots error: {e}")
            raise e



    async def _get_comments_implementation(self, request: NoteRequest) -> List[CommentInfo]:
        """实现获取评论的具体逻辑"""
        try:
            note_url_info = parse_note_info_from_note_url(request.link)
            
            comments = await self.xhs_client.get_note_all_comments(
                note_id=note_url_info.note_id,
                crawl_interval=random.random(),
                xsec_token=note_url_info.xsec_token
            )
            
            return comments or []

        except Exception as e:
            utils.logger.error(f"[XHSApiCore._get_comments_implementation] Get comments error: {e}")
            raise e

    async def _get_sub_comments_implementation(self, request: NoteRequest) -> List[SubCommentInfo]:
        """实现获取子评论的具体逻辑"""
        try:
            note_url_info = parse_note_info_from_note_url(request.link)
            
            sub_comments = await self.xhs_client.get_note_sub_comments(
                note_id=note_url_info.note_id,
                crawl_interval=random.random(),
                xsec_token=note_url_info.xsec_token
            )
            
            return sub_comments or []
        except Exception as e:
            utils.logger.error(f"[XHSApiCore._get_sub_comments_implementation] Get sub comments error: {e}")
            raise e
    
    async def _get_user_info_implementation(self, request: UserRequest) -> Optional[UserInfo]:
        """实现获取用户信息的具体逻辑"""
        try:
            creator_url_info = parse_creator_info_from_creator_url(request.link)
            
            creator_info = await self.xhs_client.get_creator_info(
                user_id=creator_url_info.creator_id,
                xsec_token=creator_url_info.xsec_token,
                xsec_source=creator_url_info.xsec_source,
            )
            
            if not creator_info:
                raise Exception("Failed to get creator info")

            return creator_info

        except Exception as e:
            utils.logger.error(f"[XHSApiCore._get_user_info_implementation] Get user info error: {e}")
            raise e

    # ===============================工具方法===============================================

    async def _get_note_detail_task(
        self,
        note_id: str,
        xsec_source: str,
        xsec_token: str,
        semaphore: asyncio.Semaphore,
        enable_comments: bool = False
    ) -> Optional[Dict]:
        """获取笔记详情的异步任务"""
        async with semaphore:
            try:
                # 先尝试从HTML获取
                note_detail = await self.xhs_client.get_note_by_id_from_html(
                    note_id, xsec_source, xsec_token
                )
                if note_detail:
                    # 直接返回原始数据，不在这里转换
                    return note_detail

                # 如果HTML获取失败，尝试从API获取
                note_detail = await self.xhs_client.get_note_by_id(
                    note_id, xsec_source, xsec_token
                )
                if note_detail:
                    # 直接返回原始数据，不在这里转换
                    return note_detail

                return None

            except Exception as e:
                utils.logger.error(f"[XHSApiCore._get_note_detail_task] Get note detail error: {e}")
                return None 